package gbsip

import (
	"fmt"
	"strconv"
	"time"

	"gitee.com/tonyjia/gb28181/pkg/cache"
	"gitee.com/tonyjia/gb28181/pkg/cronjob"
	model "gitee.com/tonyjia/gb28181/pkg/model"
	constant2 "gitee.com/tonyjia/gb28181/pkg/model/constant"
	"gitee.com/tonyjia/gb28181/pkg/parser"
	"gitee.com/tonyjia/gb28181/pkg/util"
	"github.com/ghettovoice/gosip/sip"
	"github.com/pkg/errors"
	"github.com/robfig/cron/v3"
	log "github.com/sirupsen/logrus"
)

// Cmd SIP协议的指令结构
type cmd struct {
	server *Server
}

var c *cmd

func mustSetupCommand(server *Server) {
	c = &cmd{server: server}
}

// 设备注册
type SipCommander struct {
	// 国标信息
	SipInfo model.SipServerInfo
	// 系统设备信息
	SysInfo model.SipDeviceInfo
}

func (sipClient SipCommander) Register(dest model.SipServerInfo, registerFlag int) error {
	//区分注册和注销
	if registerFlag < 0 {
		return errors.New("参数错误，注册有效期不能小于0")
	}
	request, err := sipRequestFactory.CreateRegisterRequest(sipClient.SipInfo, dest, registerFlag)
	if err != nil {
		return err
	}
	//log.Infof("发送Register请求：%+v", request)
	tx, err := c.server.sendRequest(request)
	if err != nil {
		log.Errorf("失败：%v", err)
		return err
	}
	resp := getResponse(tx)
	if resp == nil {
		return errors.New("获取相应失败")
	}

	//进行鉴权
	if resp.StatusCode() == 401 {
		req, err := sipRequestFactory.CreateRegisterRequestWithAuth(sipClient.SipInfo, dest, resp, registerFlag)
		if err != nil {
			return err
		}
		tx, err := c.server.sendRequest(req)
		resp := getResponse(tx)
		if resp.StatusCode() != 200 {
			log.Error("再次发送Register请求返回结果为：%v", resp)
			return errors.New("注册失败")
		}
	}
	//注册成功，需要进行定时任务，进行定时心跳保活
	c := cronjob.GetCron()
	if registerFlag > 0 {
		decvie := model.Device{
			DeviceID: dest.DeviceID,
			Region:   dest.ReginID,
			Host:     dest.IP,
			Port:     strconv.Itoa(dest.Port),
		}
		err = CronRegister(c, 60, decvie, dest)
		if err != nil {
			log.Errorf("定时任务启动失败：%v", err)
		}
		c.Start()
	} else {
		c.Stop() //注销的时候停止定时任务
	}
	return nil
}

// GetDeviceInfo  查询设备信息
func (sipClient SipCommander) GetDeviceInfo(d model.Device) error {
	err := QueryDevice(d)
	if err != nil {
		log.Errorf("查询设备信息失败，%v", err)
		return err
	}
	return nil
}

// GetChannelInfo 查询设备目录
func (sipClient SipCommander) GetChannelInfo(device model.Device) error {
	err := QueryCatalog(device)
	if err != nil {
		return err
	}
	return nil
}

func QueryDevice(device model.Device) error {
	xml, err := parser.CreateQueryXML(parser.DeviceInfoCmdType, device.DeviceID)
	if err != nil {
		return err
	}

	request := sipRequestFactory.createMessageRequest(device, xml)
	// log.Infof("发送设备查询信息：\n%s", request)
	tx, err := c.server.sendRequest(request)
	if err != nil {
		log.Error(err)
		return err
	}

	response := getResponse(tx)
	if response == nil || !response.IsSuccess() {
		log.Error("接收目录消息确认超时")
		return errors.New("接收目录消息确认超时")
	}
	return nil
}

func QueryCatalog(device model.Device) error {
	xml, err := parser.CreateQueryXML(parser.CatalogCmdType, device.DeviceID)
	if err != nil {
		return err
	}

	request := sipRequestFactory.createMessageRequest(device, xml)
	log.Infof("发送设备目录查询信息：\n%s", request)
	tx, err := c.server.sendRequest(request)
	if err != nil {
		log.Error(err)
		return err
	}

	response := getResponse(tx)
	if response == nil || !response.IsSuccess() {
		log.Error("接收目录消息确认超时")
		return errors.New("接收目录消息确认超时")
	}
	return nil
}

// SubscribeCatalog 订阅目录
func (sipClient SipCommander) SubscribeCatalog(device model.Device) error {
	xml, err := parser.CreateQueryXML(parser.CatalogCmdType, device.DeviceID)
	if err != nil {
		return errors.Wrap(err, "创建目录请订阅求body失败")
	}

	serverDevice := model.Device{
		DeviceID: sipClient.SipInfo.DeviceID,
		Region:   sipClient.SipInfo.ReginID,
		Host:     sipClient.SipInfo.IP,
		Port:     strconv.Itoa(sipClient.SipInfo.Port),
	}

	request, err := sipRequestFactory.createSubscribeRequest(serverDevice, xml, string(parser.CatalogCmdType))
	if err != nil {
		return errors.Wrap(err, "创建目录订阅请求失败")
	}
	tx, err := c.server.sendRequest(request)
	if err != nil {
		log.Error(err)
		return errors.Wrap(err, "发送目录订阅请求失败")
	}
	response := getResponse(tx)
	if response == nil || !response.IsSuccess() {
		log.Error("接收目录订阅消息确认超时")
		return errors.New("接收目录订阅消息确认超时")
	}
	return nil
}

// NotifyCatalog 目录通知
func (sipClient SipCommander) NotifyCatalog(channel []model.Channel, device model.Device) error {
	xml, err := parser.CreateCatalogNotifyXML(parser.CatalogCmdType, channel, device.DeviceID)
	if err != nil {
		return errors.Wrap(err, "创建目录请订阅求body失败")
	}
	request, err := sipRequestFactory.createSubscribeRequest(device, xml, string(parser.CatalogCmdType))
	if err != nil {
		return errors.Wrap(err, "创建目录订阅请求失败")
	}
	tx, err := c.server.sendRequest(request)
	if err != nil {
		log.Error(err)
		return errors.Wrap(err, "发送目录订阅请求失败")
	}
	response := getResponse(tx)
	if response == nil || !response.IsSuccess() {
		log.Error("接收目录订阅消息确认超时")
		return errors.New("接收目录订阅消息确认超时")
	}
	return nil
}

// SubscribeKeepalive 保活目录
func (sipClient SipCommander) SubscribeKeepalive(device model.Device) error {
	xml, err := parser.CreateQueryXML(parser.KeepaliveCmdType, device.DeviceID)
	if err != nil {
		return errors.Wrap(err, "创建保活请订阅求body失败")
	}

	serverDevice := model.Device{
		DeviceID: sipClient.SipInfo.DeviceID,
		Region:   sipClient.SipInfo.ReginID,
		Host:     sipClient.SipInfo.IP,
		Port:     strconv.Itoa(sipClient.SipInfo.Port),
	}
	request, err := sipRequestFactory.createSubscribeRequest(serverDevice, xml, string(parser.KeepaliveCmdType))
	if err != nil {
		return errors.Wrap(err, "创建保活订阅请求失败")
	}
	tx, err := c.server.sendRequest(request)
	if err != nil {
		log.Error(err)
		return errors.Wrap(err, "发送保活订阅请求失败")
	}
	response := getResponse(tx)
	if response == nil || !response.IsSuccess() {
		log.Error("接收保活订阅消息确认超时")
		return errors.New("接收保活订阅消息确认超时")
	}
	return nil
}

// NotifyKeepalive 保活通知
func (sipClient SipCommander) NotifyKeepalive(device model.Device) error {
	xml, err := parser.CreateNotifyXML(parser.KeepaliveCmdType, device.DeviceID)
	if err != nil {
		return errors.Wrap(err, "创建保活请订阅求body失败")
	}
	request, err := sipRequestFactory.createNotifyRequest(device, xml, string(parser.KeepaliveCmdType))
	if err != nil {
		return errors.Wrap(err, "创建保活订阅请求失败")
	}
	tx, err := c.server.sendRequest(request)
	if err != nil {
		log.Error(err)
		return errors.Wrap(err, "发送保活订阅请求失败")
	}
	response := getResponse(tx)
	if response == nil || !response.IsSuccess() {
		log.Error("接收保活通知消息确认超时")
		return errors.New("接收保活通知消息确认超时")
	}
	return nil
}

// Play 播放视频
func (sipClient SipCommander) Play(device model.Device, detail model.MediaServer, channel model.Channel) (model.StreamInfo, error) {
	ssrc := util.GetSSRC(util.RealTime)
	streamId := fmt.Sprintf("%s_%s", device.DeviceID, channel.DeviceId)
	log.Infof("点播开始，流id: %s, 设备ip: %s, SSRC: %s, rtp端口: %d\n", streamId, device.Host, ssrc, detail.Port)
	request := sipRequestFactory.createInviteRequest(device, detail, channel.DeviceId, ssrc)
	log.Infof("发送invite请求：\n%s", request)
	tx, err := c.server.sendRequest(request)
	if err != nil {
		return model.StreamInfo{}, err
	}

	resp := getResponse(tx)
	if resp == nil {
		return model.StreamInfo{}, errors.New("response is null")
	}
	log.Infof("收到invite响应：\n%s", resp)
	log.Infof("\ntransaction key: %s", tx.Key().String())

	ackRequest := sip.NewAckRequest("", request, resp, "", nil)
	ackRequest.SetRecipient(request.Recipient())
	ackRequest.AppendHeader(&sip.ContactHeader{
		Address: request.Recipient(),
		Params:  nil,
	})

	log.Infof("发送ack确认：%s\n", ackRequest)
	err = c.server.s.Send(ackRequest)
	if err != nil {
		log.Errorf("发送ack失败", err)
		return model.StreamInfo{}, errors.WithMessage(err, "send play SipOption ack request fail")
	}

	// save stream info and SipOption transaction to cache
	info := model.MustNewStreamInfo(detail.Port, detail.IP, streamId, ssrc)
	saveStreamInfo(info)

	callId, fromTag, toTag, branch, err := getRequestTxField(request, resp)
	if err != nil {
		return model.StreamInfo{}, err
	}
	streamSessionManage.saveStreamSession(device.DeviceID, channel.DeviceId, ssrc, callId, fromTag, toTag, branch)

	return info, nil
}

// StopPlay 停止播放视频
func (sipClient SipCommander) StopPlay(channel model.Channel, device model.Device) error {
	// delete stream info in cache
	streamId := fmt.Sprintf("%s_%s", device.DeviceID, channel.DeviceId)
	key := fmt.Sprintf("%s:%s", constant2.StreamInfoPrefix, streamId)
	err := cache.GetCache().Del(key)
	if err != nil {
		return err
	}

	// get SipOption tx in cache
	txInfo, err := streamSessionManage.getTx(device.DeviceID, channel.DeviceId)
	if err != nil {
		return err
	}

	// generate bye request send to device
	byeRequest, err := sipRequestFactory.createByeRequest(channel.DeviceId, device, txInfo)
	if err != nil {
		return err
	}

	log.Infof("创建Bye请求：\n%s", byeRequest)
	key = fmt.Sprintf("%s:%s", constant2.StreamTransactionPrefix, streamId)

	//err = s.s.Send(byeRequest)
	tx, err := c.server.sendRequest(byeRequest)
	if err != nil {
		log.Error("发送请求发生错误,", err)
	}

	response := getResponse(tx)

	if response == nil {
		log.Error("response is nil")
	}
	return nil
}

// 通道信息查询返回
func QueryMessageResponse(user, host, port, body string) error {
	request := sipRequestFactory.createMessageResponse(user, host, port, body)
	log.Infof("查询设备信息响应：%v\n", request)
	tx, err := c.server.sendRequest(request)
	if err != nil {
		log.Error("发送请求发生错误,", err)
	}
	response := getResponse(tx)
	if response == nil || !response.IsSuccess() {
		log.Error("接收目录消息确认超时")
		return errors.New("接收目录消息确认超时")
	}
	return nil
}

// save stream info to cache
func saveStreamInfo(info model.StreamInfo) {
	key := fmt.Sprintf("%s:%s", constant2.StreamInfoPrefix, info.Stream)
	//cache.Set(key, info)
	log.Infof("key: %s", key)
}

func CronRegister(cron *cron.Cron, t int, device model.Device, dest model.SipServerInfo) error {
	timeDuration := fmt.Sprintf("@every %ds", t)
	_, err := cron.AddFunc(timeDuration, func() {
		log.Info("cron job handler " + time.Now().Format("2006-01-02 15:04:05"))
		//简化直接发送在线
		xml, err := parser.CreateDeviceStatusXML(parser.KeepaliveCmdType, device.DeviceID, constant2.ResultOK)
		if err != nil {
			log.Errorf("构造内容失败:%v", err)
		}
		request := sipRequestFactory.createMessageRequest(device, xml)
		_, err = c.server.sendRequest(request)
		if err != nil {
			log.Error(err)
		}
	})

	return err
}

// get SipOption tx info by SipOption request and response
func getRequestTxField(request sip.Request, response sip.Response) (callId, fromTag, toTag, viaBranch string, err error) {
	callID, ok := request.CallID()
	if !ok {
		return "", "", "", "", errors.New("get CallId header in request fail")
	}

	fromHeader, ok := request.From()
	if !ok {
		return "", "", "", "", errors.New("get from header in request fail")
	}
	ft, ok := fromHeader.Params.Get("tag")
	if !ok {
		return "", "", "", "", errors.New("get tag field in 'from' header fail")
	}

	toHeader, ok := response.To()
	if !ok {
		return "", "", "", "", errors.New("get to header in request fail")
	}
	tg, ok := toHeader.Params.Get("tag")
	if !ok {
		return "", "", "", "", errors.New("get tag field in 'to' header fail")
	}

	viaHop, ok := request.ViaHop()
	if !ok {
		return "", "", "", "", errors.New("get via header in request fail")
	}

	branch, ok := viaHop.Params.Get("branch")
	if !ok {
		return "", "", "", "", errors.New("get branch field in 'via' header fail")
	}

	callId = callID.Value()
	fromTag = ft.String()
	toTag = tg.String()
	viaBranch = branch.String()
	return
}
